/**
 * @file cx_utility.h
 * @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
 */

#ifndef CX_UTILITY_H
#define CX_UTILITY_H

#include <stddef.h>
#include <stdint.h>
#include <time.h>

#ifdef __cplusplus
extern "C" {
#endif

struct tm *cx_util_localtime(void);

void cx_util_localtime_print(char time_str[13]);        // in format yyMMddhhmmss
void cx_util_utc_to_beijing(struct tm *time);

unsigned short cx_util_crc16(unsigned short crc, unsigned char const *buffer, int len);

/* file manipulate */
int cx_util_get_folder_size(char *path);
int cx_util_force_rmdir(char *path);


/* slist */
typedef struct cx_slist_s {
    struct cx_slist_s *next;
} cx_slist_t;

#define cx_offsetof(type, member)   ((size_t)&(((type *)0)->member))

#define cx_container_of(ptr, type, member) \
    ((type *) ((char *) (ptr) - cx_offsetof(type, member)))

/*
* Iterate over list of given type.
*
* @param[in]   queue   he head for your list.
* @param[in]   node    the type * to use as a loop cursor.
* @param[in]   type    the type of the struct this is embedded in.
* @param[in]   member  the name of the slist_t within the struct.
*/
#define cx_slist_for_each_entry(queue, node, type, member)        \
    for (node = (queue)->next? cx_container_of((queue)->next, type, member) : NULL; \
         node;                                        \
         node = node->member.next ? cx_container_of(node->member.next, type, member) : NULL)

/*
 * Iterate over list of given type safe against removal of list entry.
 *
 * @param[in]   queue   the head for your list.
 * @param[in]   tmp     the type * to use as a temp.
 * @param[in]   node    the type * to use as a loop cursor.
 * @param[in]   type    the type of the struct this is embedded in.
 * @param[in]   member  the name of the slist_t within the struct.
 */
#define cx_slist_for_each_entry_safe(queue, tmp, node, type, member) \
    for (node = (queue)->next? cx_container_of((queue)->next, type, member) : NULL, \
         tmp = (queue)->next ? (queue)->next->next : NULL;        \
         node;                                           \
         node = tmp ? cx_container_of(tmp, type, member) : NULL, tmp = tmp ? tmp->next : NULL)

static inline void cx_slist_add(cx_slist_t *node, cx_slist_t *head)
{
    node->next = head->next;
    head->next = node;
}

void cx_slist_add_tail(cx_slist_t *node, cx_slist_t *head);
void cx_slist_del(cx_slist_t *node, cx_slist_t *head);
int cx_slist_entry_number(cx_slist_t *queue);

typedef struct {
    uint8_t *buffer;
    uint32_t length;
    uint32_t ridx;
    uint32_t widx;
} cx_dev_ringbuf_t;

/**
 * @brief  create a ring buffer
 * @param[in] length length space of the ring buffer
 * @return  pointer to ring buffer
 */
int cx_util_ringbuffer_create(cx_dev_ringbuf_t *ringbuffer, char *buffer, int length);

/**
 * @brief   destroy the ring buffer
 * @param[in] buffer pointer to the ring buffer
 * @return  
 */
int cx_util_ringbuffer_destroy(cx_dev_ringbuf_t *buffer);

/**
 * @brief   read data from ring buffer.
 * @param[in] buffer pointer to the ring buffer
 * @param[in] target pointer to buffer for data to read from ring buffer
 * @param[in] amount amount of data items to read
 * @return  number of actual read data
 */
int cx_util_ringbuffer_read(cx_dev_ringbuf_t *buffer, uint8_t *target, uint32_t amount);

/**
 * @brief   write data to ring buffer
 * @param[in] buffer pointer to the ring buffer
 * @param[in] data pointer to buffer for data to write to ring buffer
 * @param[in] length length of data items to write
 * @return  0 for success
 */
int cx_util_ringbuffer_write(cx_dev_ringbuf_t *buffer, uint8_t *data, uint32_t length);

/**
 * @brief   is the ring buffer empty?
 * @param[in] buffer pointer to the ring buffer
 * @return  0 for success
 */
int cx_util_ringbuffer_empty(cx_dev_ringbuf_t *buffer);
/**
 * @brief   is the ring buffer full?
 * @param[in] buffer pointer to the ring buffer
 * @return  0 for success
 */
int cx_util_ringbuffer_full(cx_dev_ringbuf_t *buffer);
/**
 * @brief   available write space to ring buffer
 * @param[in] buffer pointer to the ring buffer
 * @return   number of write spcae
 */
int cx_util_ringbuffer_available_write_space(cx_dev_ringbuf_t *buffer);
/**
 * @brief    available read space to ring buffer
 * @param[in] buffer pointer to the ring buffer
 * @return  number of read spcae
 */
int cx_util_ringbuffer_available_read_space(cx_dev_ringbuf_t *buffer);

#define cx_util_ringbuffer_available_write_space(B) (\
        (B)->length - cx_util_ringbuffer_available_read_space(B))

#define cx_util_ringbuffer_full(B) (cx_util_ringbuffer_available_write_space(B) == 0)

#define cx_util_ringbuffer_empty(B) (cx_util_ringbuffer_available_read_space((B)) == 0)

#define cx_util_ringbuffer_clear(B) ((B)->widx = (B)->ridx = 0)

/** @} */

#ifdef __cplusplus
}
#endif

#endif /* CX_UTILITY_H */

